#!/usr/bin/env python
# -*- coding: ISO-8859-15 -*-
#
# Copyright (C) 2007 Rafael S. Gonzalez D'Leon <rafaeldleon@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA

from sshproxy.config import get_config
from sshproxy.acl import ACLRule
from sshproxy.site import SiteDB, SiteInfo

from config import LDAPConfigSection
from ldapops import LDAPDB

class LDAPSiteConfigSection(LDAPConfigSection):
    section_id = 'site_db.ldap'

LDAPSiteConfigSection.register()


class LDAPSiteInfo(SiteInfo, LDAPDB):
    _db_handler = 'site_db'
    def __reginit__(self, login, name, **kw):
        self.open_db()
        self._sid = 0
        self._lid = 0
        SiteInfo.__reginit__(self, login, name, **kw)

    def load(self):
        query = '(&(ObjectClass=sshProxySite)(cn=%s))' % self.name
        
        result = self.ldap_get(query)
        if not result:
            return
        sitedn, userattrs = result
        self._sid = sitedn
        port = userattrs['ipServicePort'][0]
        name = userattrs['cn'][0]
        ip_add_ready= False
        try: 
            ip_address = userattrs['ipHostNumber'][0]
            ip_add_ready = True
           
        except:
            pass
           
        query = '(&(ObjectClass=sshProxySite)(cn=%s))' % self.name
        
        tags = {}
        result = self.ldap_get(query)
        sitedn, userattrs = result
        try: #This try was added to workaround an error passing by sshProxyTags when there isn't anyone
            acltags = userattrs['sshProxyTags']
            for tagentry in acltags:
                tag,value = tagentry.split(' = ')
                
                if value.startswith('--'):
                    newvalue=value.split('--')[1]
                    tagstodelete =[('sshProxyTags',tag+' = '+newvalue)]
                    self.ldap_del_attrs(sitedn,tagstodelete)
                elif len(value):
                    if tags.has_key(tag):
                        tags[tag]=tags[tag]+' '+value
                    else:
                        tags[tag] = value
                else:
                    tagstodelete=[]
                    for tagentrytemp in acltags:
                        tagtemp,valuetemp = tagentrytemp.split(' = ')
                        if tagtemp == tag:
                            tagstodelete.append(('sshProxyTags',tagentrytemp))
                    
                    self.ldap_del_attrs(sitedn,tagstodelete)
                    
        except:
            pass
       
        self.s_tokens.update(tags)

        # TODO: handle the default case, see also in file backend
        
        if self.login == 'None':
            query = '(ObjectClass=sshProxySiteLogin)'
        else:
            query = '(&(ObjectClass=sshProxySiteLogin)(cn=%s))' % self.login
        loginlist = self.ldap_list(query,self._sid)
        if loginlist:
            priorityresult = 0
            loginresult = loginlist[0]
            for logininfo in loginlist:
                try:
                    priorityop = logininfo[1]['sshProxyPriority'][0]
                except:
                    priorityop = 0
                if priorityop > priorityresult:
                    priorityresult = priorityop
                    loginresult = logininfo
            self._lid = logininfo[0]
            userdn = self._lid
            login = logininfo[1]['cn']
            priority = priorityresult
            userpass_ready= False
            try:
                password = '$blowfish$'+logininfo[1]['userPassword'][0].split('{blowfish}')[1]
                userpass_ready= True
            except:
                #raise
                pass
            pkey_ready= False
            try:
                pkey = logininfo[1]['sshPublicKey'][0]
                pkey_ready= True
            except:
                pass
            
            tags = {'login': login, 'priority': priority}
            if userpass_ready:
                tags['password'] = password
            if pkey_ready:
                tags['pkey'] = pkey
                
            self.l_tokens.update(tags)
            
            tags = {}
            try: #This try was added to workaround an error passing by sshProxyTags when there isn't anyone
                acltags = logininfo[1]['sshProxyTags']
                for tagentry in acltags:
                    tag,value = tagentry.split(' = ')
                    if value.startswith('--'):
                        newvalue=value.split('--')[1]
                        tagstodelete =[('sshProxyTags',tag+' = '+newvalue)]
                        self.ldap_del_attrs(userdn,tagstodelete)
                    elif len(value):
                        if tags.has_key(tag):
                            tags[tag]=tags[tag]+' '+value
                        else:
                            tags[tag] = value
                        
                    else:
                        tagstodelete=[]
                        for tagentrytemp in acltags:
                            tagtemp,valuetemp = tagentrytemp.split(' = ')
                            if tagtemp == tag:
                                tagstodelete.append(('sshProxyTags',tagentrytemp))
                    
                        self.ldap_del_attrs(userdn,tagstodelete)
                        
            except:
                pass
            self.l_tokens.update(tags)    
            
            self.loaded = True    
       
        tags = {'name': name, 'port': port}
        if ip_add_ready:
            tags['ip_address'] = ip_address
            

        self.s_tokens.update(tags)

        self.loaded = True


    def save(self):
        sid = self._sid
        lid = self._lid
        if sid is None:
            return

        if not self.login:
            tok = self.s_tokens
            modlist = [('objectClass',['ipService', 'top', 'sshProxySite']), ('cn',self.name),
                       ('ipServicePort', tok.get('port', '22')),('ipServiceProtocol','ssh')]
            self.ldap_update(sid,modlist)
            ipaddresstmp = tok.get('ip_address', '')
            if ipaddresstmp != '':
                modlist =[]
                modlist.append(('objectClass', ['ipHost','top']))
                modlist.append(('ipHostNumber', ipaddresstmp))
                self.ldap_update(sid,modlist)
                #modlist[0] = ('objectClass',['ipService', 'ipHost', 'top', 'sshProxySite'])
                #modlist.append(('ipHostNumber', ipaddresstmp))
            
            #self.ldap_update(sid,modlist)
            
            for tag, value in self.s_tokens.items():
                if tag in ('name', 'ip_address', 'port'):
                    continue
                elif value.startswith('--'):
                    newvalue=value.split('--')[1]
                    tagstodelete =[('sshProxyTags',tag+' = '+newvalue)]
                    self.ldap_del_attrs(sid,tagstodelete)
                elif value and len(str(value)):
                    valuelist=value.split(' ')
                    for onevalue in valuelist:
                        self.ldap_update(sid,[('sshProxyTags',tag+' = '+onevalue)])
                    
                else:
                    tagstodelete=[]
                    result = self.ldap_get('(objectClass=*)',sid)
                    proxytagstemp = result[1]['sshProxyTags']
                    for tagentrytemp in proxytagstemp:
                        tagtemp,valuetemp = tagentrytemp.split(' = ')
                        
                        if tagtemp == tag:
                            tagstodelete.append(('sshProxyTags',tagtemp+' = '+valuetemp))
                    
                    self.ldap_del_attrs(sid,tagstodelete)
                   
        
        else:
            lid = self._lid
            if not lid:
                return
    
            tok = self.l_tokens
            
            modlist = [('objectClass',['inetOrgPerson', 'top', 'sshProxySiteLogin']), ('cn',self.login),('sn',self.login)]
            pkeytmp = tok.get('pkey', '')
            if pkeytmp != '':
                modlist[0] = ('objectClass',['inetOrgPerson', 'top', 'sshProxySiteLogin','ldapPublicKey'])
                modlist.append(('sshPublicKey', pkeytmp))
                modlist.append(('uid', self.login))
            passwordtmp = tok.get('password', '')
            if passwordtmp != '':
                if passwordtmp.startswith('$blowfish$'):
                    password = '{blowfish}'+passwordtmp.split('$blowfish$')[1]
                else:
                    password = passwordtmp
                modlist.append(('userPassword', password))
            prioritytmp = tok.get('priority', '')
            if prioritytmp != '':
                modlist.append(('sshProxyPriority', prioritytmp))
	    self.ldap_update(lid,modlist)
            
            for tag, value in self.l_tokens.items():
                if tag in ('login', 'password', 'pkey', 'priority',
                                                'ip_address', 'port'):
                    continue
                elif value.startswith('--'):
                    newvalue=value.split('--')[1]
                    tagstodelete =[('sshProxyTags',tag+' = '+newvalue)]
                    self.ldap_del_attrs(lid,tagstodelete)
                elif value and len(str(value)):
                    valuelist=value.split(' ')
                    for onevalue in valuelist:
                        self.ldap_update(lid,[('sshProxyTags',tag+' = '+onevalue)])
                    
                else:
                    tagstodelete=[]
                    result = self.ldap_get('(objectClass=*)',lid)
                    proxytagstemp = result[1]['sshProxyTags']
                    for tagentrytemp in proxytagstemp:
                        tagtemp,valuetemp = tagentrytemp.split(' = ')
                       
                        if tagtemp == tag:
                            tagstodelete.append(('sshProxyTags',tagtemp+' = '+valuetemp))
                    
                    self.ldap_del_attrs(lid,tagstodelete)
                    


class LDAPSiteDB(SiteDB, LDAPDB):
    _db_handler = 'site_db'
    def __reginit__(self, **kw):
        self.open_db()
        SiteDB.__reginit__(self, **kw)

    def list_site_users(self, **tokens):
        sites = []
        query = '(ObjectClass=sshProxySite)'
        
        
        try:
            listsites=self.ldap_list(query)
        except:
            return 'An error ocurred when getting sites'
        for sitedn, userattrs in listsites:
            sites.append((sitedn, userattrs['cn'][0]))
        
       
        sitelogins=[]
        for id,name in sites:
            
            query = '(ObjectClass=sshProxySiteLogin)'
            logins =[]
            try:
                listlogins=self.ldap_list(query,id)
            except:
                return 'An error ocurred when getting user logins of %s' % name
            if listlogins:
                for logindn, userattrs in listlogins:
                    logins.append(SiteInfo(userattrs['cn'][0],name))
        
        
            if not len(logins):
                logins.append(SiteInfo('ORPHAN', name, priority=0))

            sitelogins += logins
        sites = sitelogins
        filter = tokens.get('filter', tokens.get('f', None))
        if filter:
            siteinfos = []
            for site in sites:
                if ACLRule('list_site_users_filter',
                            filter).eval(namespace={'site':site.get_tags()}):
                    siteinfos.append(site)

            sites = siteinfos

        return sites

    def exists(self, sitename, **tokens):
        login, site = self.split_user_site(sitename)

        if login == '*':
            login = None

        query = '(&(ObjectClass=sshProxySite)(cn=%s))' % site
        result = self.ldap_get(query)
        if result:
            id,attrs = result
        else:
            id = None
        
        if not id:
            return False

        if not login:
            return id
        query = '(&(ObjectClass=sshProxySiteLogin)(cn=%s))' % login
        result = self.ldap_get(query, id)
        if result:
            lid,attrs = result
            return lid
        else:
            return False
        

    def add_site(self, sitename, **tokens):
        login, site = self.split_user_site(sitename)

        if login == '*':
            return "'*' is not allowed, be more specific."

        if not login:
            if self.exists(site, **tokens):
                return 'Site %s does already exist' % site
            cfg = get_config('%s.ldap' % self._db_handler)
            base_dn = cfg['base_dn']
            sitedn = 'cn=%s,'% sitename + base_dn
            # create site
            port = tokens.get('port', 22)
            try:
                port = int(port)
                if not (0 < port < 65536):
                    raise ValueError
            except ValueError:
                return ('Port must be numeric and have a strictly positive '
                        'value inferior to 65536')
            
            modlist = [('objectClass',['ipService', 'top', 'sshProxySite']),('cn',site),('ipServicePort',str(port))
                       ,('ipServiceProtocol','ssh')]
            ipaddrtmp = tokens.get('ip_address', '')
            if ipaddrtmp != '':
                modlist[0] = ('objectClass',['ipService', 'ipHost', 'top', 'sshProxySite'])
                modlist.append(('ipHostNumber', ipaddrtmp))
            try:
                self.ldap_add(sitedn,modlist)
            except:
                return 'A problem occured when adding site %s' % sitename
            
        elif not self.exists(site, **tokens):
            # if site does not exist and a login was given, exit with an error
            return 'Please create site %s first' % site
        
        elif not SiteInfo(login, site).get_tags()['ip_address']:
            return 'Please add an ip_address tag to site %s first' % site
        else:
            if self.exists(sitename, **tokens):
                
                return 'Site %s does already exist' % sitename

            query = '(&(ObjectClass=sshProxySite)(cn=%s))' % site
            result = self.ldap_get(query)
            sid,attrs = result
            lid = 'cn=%s,'% login+sid
            
            modlist = [('objectClass',['inetOrgPerson', 'top', 'sshProxySiteLogin']),('cn',login),('sn',login)]
            passwordtmp = tokens.get('password', '')
            if passwordtmp != '':
                if passwordtmp.startswith('$blowfish$'):
                    password = '{blowfish}'+passwordtmp.split('$blowfish$')[1]
                else:
                    password = passwordtmp
                modlist.append(('userPassword', password))
            try:
                self.ldap_add(lid,modlist)
            except:
                return 'A problem occured when adding site %s' % sitename
            

        site = SiteInfo(login, site, **tokens)
        site.save()
        return 'Site %s added' % sitename

    def _del_login(self, login_dn, **tokens):
        
        self.ldap_del(login_dn)
        

    def del_site(self, sitename, **tokens):
        login, site = self.split_user_site(sitename)

        if login == '*':
            sitename = name

        sid = self.exists(sitename, **tokens)
        if not sid:
            return 'Site %s does not exist' % sitename

        if login:
            ret = False
            if login == '*':
                query = '(ObjectClass=sshProxySiteLogin)'
                result = self.ldap_list(query,sid)
                for logindn,attrs in result:
                    self._del_login(logindn)
                    ret = True
                sitename = '*@%s' % name

            else:
                lid = self.exists(sitename, **tokens)
                if lid:
                    self._del_login(lid, **tokens)
                    ret = True

            if not ret:
                return 'Site %s does not exist' % sitename

            return 'Site %s deleted' % sitename

        else:
            query = '(ObjectClass=sshProxySiteLogin)'
            result = self.ldap_list(query,sid)
            if result:
                count = len(result)
            else:
                count = 0
         
            if count > 0:
                return "Site %s has still %d logins" % (sitename, count)

           
            self.ldap_del(sid)
            return 'Site %s deleted' % sitename
            

